// src/main/java/Util/SimpleJdbcTemplate.java
package com.hp.Util;                                               // 包名：Util

import javax.sql.DataSource;                                // 数据源接口
import java.sql.*;                                          // JDBC 基本类型
import java.util.ArrayList;                                 // ArrayList 实现
import java.util.List;                                      // List 接口

/**
 * SimpleJdbcTemplate：极简 JDBC 工具（不依赖 Spring）
 * 提供：
 *  - int update(sql, params...)                  增删改
 *  - long insertAndReturnId(sql, params...)      插入并返回自增主键
 *  - <T> T queryOne(sql, mapper, params...)      查询单条（查不到返回 null）
 *  - <T> List<T> query(sql, mapper, params...)   查询列表
 * 自动把 java.util.Date 参数转换为 java.sql.Timestamp 绑定到 SQL
 */
public class SimpleJdbcTemplate {                           // 工具类定义
    private final DataSource dataSource;                    // 持有数据源，负责提供连接

    public SimpleJdbcTemplate(DataSource dataSource) {      // 构造器注入数据源
        this.dataSource = dataSource;                       // 保存引用
    }

    // 通用更新（INSERT / UPDATE / DELETE），返回影响行数
    public int update(String sql, Object... params) {
        try (Connection conn = dataSource.getConnection();  // 向池子借连接（try-with-resources 自动归还）
             PreparedStatement ps = conn.prepareStatement(sql)) { // 预编译 SQL
            bind(ps, params);                               // 绑定参数
            return ps.executeUpdate();                      // 执行并返回影响行数
        } catch (SQLException e) {                          // 捕获底层异常
            throw new DaoException("Update failed: " + sql, e); // 向上抛出运行时异常
        }
    }

    // 插入并返回自增主键（适用于 MySQL 自增 id）
    public long insertAndReturnId(String sql, Object... params) {
        try (Connection conn = dataSource.getConnection();  // 借连接
             PreparedStatement ps =                         // 要求返回生成的键
                     conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
            bind(ps, params);                               // 绑定参数
            ps.executeUpdate();                             // 执行
            try (ResultSet keys = ps.getGeneratedKeys()) {  // 读取自增主键
                if (keys.next()) return keys.getLong(1);    // 返回第一列主键
                throw new DaoException("No generated key returned for SQL: " + sql); // 正常不该发生
            }
        } catch (SQLException e) {                          // 异常处理
            throw new DaoException("Insert failed: " + sql, e);
        }
    }

    // 查询单条记录：查不到返回 null
    public <T> T queryOne(String sql, RowMapper<T> mapper, Object... params) {
        List<T> list = query(sql, mapper, params);          // 复用列表查询
        return list.isEmpty() ? null : list.get(0);         // 无则 null，有则取第一条
    }

    // 查询列表：把每一行用 mapper 映射成对象
    public <T> List<T> query(String sql, RowMapper<T> mapper, Object... params) {
        try (Connection conn = dataSource.getConnection();      // 借连接
             PreparedStatement ps = conn.prepareStatement(sql)) { // 预编译
            bind(ps, params);                                   // 绑定参数
            try (ResultSet rs = ps.executeQuery()) {            // 执行查询
                List<T> out = new ArrayList<>();                // 结果集合
                while (rs.next()) out.add(mapper.map(rs));      // 逐行映射
                return out;                                     // 返回列表
            }
        } catch (SQLException e) {                              // 异常处理
            throw new DaoException("Query failed: " + sql, e);  // 包装并抛出
        }
    }

    // 统一参数绑定：自动识别 java.util.Date 并转成 Timestamp
    private void bind(PreparedStatement ps, Object... params) throws SQLException {
        if (params == null) return;                             // 无参数直接返回
        for (int i = 0; i < params.length; i++) {               // 遍历参数
            Object p = params[i];                               // 当前参数
            if (p instanceof java.util.Date) {                  // 如果是 java.util.Date
                ps.setTimestamp(i + 1, new Timestamp(((java.util.Date) p).getTime())); // 转 Timestamp 再绑定
            } else {
                ps.setObject(i + 1, p);                         // 其他类型交给驱动处理（Long/String/Boolean/Integer…）
            }
        }
    }
}
